home *** CD-ROM | disk | FTP | other *** search
/ Carousel Volume 2 #1 / carousel.iso / mactosh / util / hdrunner.sit / HD Runner.c < prev    next >
C/C++ Source or Header  |  1987-04-13  |  23KB  |  925 lines

  1. /*
  2.   * HD Runner:        An application that scans the desktop to retrieve the names and locations of
  3.   *                all applications on HFS volumes, and offers a scrolling list for launching.
  4.   * Author:        Dewi Williams   Delphi: dewi, CompuServe 73210,132.
  5.   * Status:        Explicitly in public domain. Please keep the application, source and
  6.   *                documentation together when distributing.
  7.   * Limitations:        ignores MFS volumes.
  8.   * Build:            Lightspeed C, version 2.01. Needs Lightspeed's qsort.c
  9.   * Version:        1.0
  10.   */
  11.  
  12. #include    <pascal.h>
  13. #include    <MacTypes.h>
  14. #include    <QuickDraw.h>
  15. #include    <ResourceMgr.h>
  16. #include    <MenuMgr.h>
  17. #include    <MemoryMgr.h>
  18. #include    <FileMgr.h>
  19. #include    <HFS.h>
  20. #include    <ToolboxUtil.h>
  21. #include    <EventMgr.h>
  22. #include    <ListMgr.h>
  23. #include    <DialogMgr.h>
  24. #include    <StdFilePkg.h>
  25. #include    <OSUtil.h>
  26. #include    <SegmentLdr.h>
  27.  
  28. /* Low memory globals */
  29. extern int    ROM85 :        0x28E;            /* ROM Version */
  30. extern int    mBarHeight :    0xBAA;            /* menu bar height: 128K ROMs and onwards... */
  31.  
  32. /* A define to test for a 64K ROM. */
  33. #define    has64K        (ROM85&0x8000)
  34.  
  35. /* A define to calculate the height of the menu bar. */
  36. #define    mbHeight        ((has64K) ? 20 : mBarHeight)
  37.  
  38. /* These two are more efficient than using the Toolbox equivalents. */
  39. #define    HiWord(x)    (((unsigned short *)&(x))[0])
  40. #define    LoWord(x)    (((unsigned short *)&(x))[1])
  41.  
  42. /* LPOINT: macro that coerces a point into a long and vice versa. */
  43. #define    LPOINT(pt)    (*((long*)&pt))
  44.  
  45. #define    MIN(a,b)        ((a < b) ?  a : b)
  46.  
  47. /* A define to round up to an even number. */
  48. #define    EVEN(x)            (( (x + 1) >> 1) << 1)
  49.  
  50. #define    NAME_SIZE        32            /* Maximum Finder name length */
  51. #define    NULL                0L
  52.  
  53. /* A define to skip around the variable-length ESTR in the APPL resource. */
  54. #define    NEXT_APPL(a)        ((Appl *)(EVEN( (ulong)(&a->name + a->name[0] + 1))))
  55.  
  56. /* Resource identities */
  57. #define    R_DLOG            128            /* The dialog box */
  58. #define    R_CORRUPT        129            /* The bad desktop alert */
  59. #define    R_NOHFS            130            /* The MFS File Manager is active alert */
  60. #define    R_VOLPROBS        131            /* Something's wrong with volumes. */
  61. #define    R_DESKTOP        128            /* The "Desktop" string */
  62. #define    R_FINDER            129            /* The "Finder" string */
  63. #define    R_ABOUT            132            /* The "About HD Runner..." dialog box */
  64. #define    R_MENUBAR        256            /* The MBAR resource. */
  65. #define    R_READING        133            /* "Reading desktop file..." dialog box */
  66. #define    R_CONFIG            128            /* The 'GNRL' config resource. */
  67.  
  68. /* Menu IDs */
  69. #define    M_ABOUT            256
  70. #define    M_FILE            257
  71. #define    M_EDIT            258
  72.  
  73. /* Dialog item numbers for the modeless dialog. */
  74. #define    I_RUN            1
  75. #define    I_QUIT            2
  76. #define    I_OUTLINE            3
  77. #define    I_APPS            4
  78. #define    I_MINIFINDER        5
  79. #define    I_DOCS            6
  80.  
  81. /* And for the "About" dialog. */
  82. #define    I_ABOUTOUTLN        5
  83.  
  84. /* Cursor keys */
  85. #define    cursUp            0x1e
  86. #define    cursDown            0x1f
  87.  
  88. /* Types */
  89. typedef unsigned long ulong;
  90.  
  91. /* The layout of the 'APPL' resource in the desktop, as determined from TechNote #29,
  92.   * and confirmed by looking at its 'TMPL' resource.
  93.   */
  94. typedef struct {
  95.     ResType        creator;
  96.     long            dir;
  97.     unsigned char    name[1];
  98. } Appl, **ApplHandle;
  99.  
  100. /* The application table entry. Essentially, it's just the Appl struct above with a fixed size name
  101.   * field to make sorting easier.
  102.   */
  103. typedef struct {
  104.     int            volID;
  105.     ResType        creator;
  106.     long            dir;
  107.     unsigned char    name[NAME_SIZE];
  108. } FullAppl;
  109.  
  110. /*
  111.   * The AppParmHandle layout for a single attached document.
  112.   */
  113. typedef struct {
  114.     int        message;
  115.     int        count;
  116.     AppFile    a;
  117. } OneArg;
  118.  
  119. /* Forward references */
  120. Handle        _Get1Resource(ResType, int);
  121. int            appCompare(FullAppl *, FullAppl *);
  122. DialogPtr        DispChooseApp();
  123. pascal void    ListDraw();
  124. int            StartsWith(Byte);
  125. void            Quit(void);
  126. pascal Boolean    ListFileFilter(ParmBlkPtr p);
  127. void            SetupDAs(void);
  128. int            DoCommand(long mResult);
  129. int            DoChar(DialogPtr dPtr, unsigned char theChar);
  130. void            About(void);
  131. int            EventLoop(DialogPtr, EventRecord *);
  132.  
  133. /* Globals */
  134. int            theSelection = 0;    /* index of currently selected application */
  135. int            numApps;            /* number of applications in list */
  136. Rect            box;                /* scrolling list's outline */
  137. ListHandle        lh;                /* List Manager handle */
  138. FullAppl        *apps = NULL;        /* The application table. */
  139. char            fName[20];        /* Holds finder name retrieved from the R_FINDER 'STR ' */
  140. Byte            miniFinder;        /* Is the Finder active? */
  141. Handle        mHandle;            /* Handle to the menu bar. */
  142.  
  143. main()
  144. {
  145.     int                rsrc;
  146.     ApplHandle        aH;
  147.     register Appl        *a;
  148.     register Appl        *end;
  149.     register FullAppl    *copy;
  150.     Str255            name;
  151.     register int        i;
  152.     int                itemHit;
  153.     OSErr            err;
  154.     WDPBRec            pb;
  155.     HParamBlockRec    v;
  156.     VolumeParam        vparm;
  157.     int                volNo;
  158.     StringHandle        desktop, finder;
  159.     Handle            config;
  160.     int                volApps;
  161.     CursHandle        watch;
  162.     int                op;
  163.     SFReply            reply;
  164.     Point                pt;
  165.     DialogPtr            dPtr;
  166.     EventRecord        evRec;
  167.     DialogPtr            reading;
  168.     int                doDeskDialog;
  169.  
  170.     /* Initialize the various managers. */
  171.     InitGraf(&thePort);
  172.     InitFonts();
  173.     FlushEvents( everyEvent, 0 );
  174.     InitWindows();
  175.     InitMenus();
  176.     TEInit();
  177.     InitDialogs(0L);
  178.     InitCursor();
  179.     MaxApplZone();    
  180.     SetupDAs();
  181.  
  182.     if (FSFCBLen == -1) {                /* Is the HFS File Manager active? */
  183.         StopAlert(R_NOHFS, (ProcPtr)NULL);
  184.         ExitToShell();
  185.     }
  186.  
  187.     if ( (finder = (StringHandle)GetResource('STR ', R_FINDER)) == NULL) {
  188.         /* ResEdit fiend on the loose? */
  189.         SysBeep(5);
  190.         ExitToShell();
  191.     }
  192.  
  193.     BlockMove(*finder, fName, **finder +1);
  194.     ReleaseResource(fName);
  195.  
  196.     /* Is the finder active? */
  197.     if ((miniFinder = IUEqualString(fName, FinderName)) != 0) {
  198.         /* Somebody's intercepting, but is it us? */
  199.         if (IUEqualString(FinderName, CurApName) != 0) {
  200.             /* Somebody else. miniFinder's cleared, and arrangements made to quit back to
  201.               * this guy, rather than the finder. This means that you can run HD Runner
  202.               * under Oasis, and then go back to Oasis afterwards.
  203.               */
  204.             BlockMove(FinderName, fName, FinderName[0] +1);
  205.             miniFinder = 0;
  206.         }
  207.     }
  208.     
  209.     if ( (config = GetResource('GNRL', R_CONFIG)) == NULL) 
  210.         doDeskDialog = 0;
  211.     else
  212.         doDeskDialog = **config;
  213.  
  214.     if (doDeskDialog) {
  215.         reading  = GetNewDialog(R_READING, (Ptr)NULL, (WindowPtr)-1);
  216.         DrawDialog(reading);        /* so that the string gets drawn. */
  217.     }
  218.  
  219.     if ( (desktop = (StringHandle)GetResource('STR ', R_DESKTOP)) == NULL) {
  220.         /* ResEdit fiend on the loose? */
  221.         SysBeep(5);
  222.         Quit();
  223.     }
  224.  
  225.     HNoPurge(desktop);                    /* just in case */
  226.     numApps = 0;
  227.  
  228.     if ( (watch = GetCursor(watchCursor)) != NULL) SetCursor(*watch);
  229.  
  230.     /* Run through all mounted volumes. */
  231.     for(volNo =1;; volNo++) {
  232.         v.volumeParam.ioCompletion = NULL;
  233.         v.volumeParam.ioNamePtr = NULL;
  234.         v.volumeParam.ioVolIndex = volNo;
  235.         v.volumeParam.ioVRefNum = 0;
  236.         
  237.         err = PBHGetVInfo(&v, FALSE);
  238.         
  239.         if (err == nsvErr) break;            /* Run through all the mounted volumes. */
  240.         
  241.         if (err != noErr) {                /* Something's twisted.  */
  242.             StopAlert(R_VOLPROBS, (ProcPtr)NULL);
  243.             Quit();
  244.         }
  245.         
  246.         /* Validate the volume as online, HFS. */
  247.         if(v.volumeParam.ioVSigWord != 0x4244) continue;    /* skip MFS */
  248.  
  249.         if(v.volumeParam.ioVDRefNum >= 0) continue;    /* offline */
  250.  
  251.         /* This volume passes the tests. Let's go for its desktop file. Go for root. */
  252.         pb.ioCompletion = NULL;
  253.         pb.ioNamePtr = NULL;
  254.         pb.ioVRefNum = v.volumeParam.ioVRefNum;
  255.  
  256.         if (PBSetVol(&pb, FALSE) != noErr) {
  257.             /* No such volume? I suspect that things are twisted, and there's no point
  258.               * in continuing.
  259.               */
  260.             StopAlert(R_VOLPROBS, (ProcPtr)NULL);
  261.             Quit();
  262.         }
  263.  
  264.         HLock(desktop);
  265.         SetResLoad(FALSE);
  266.         if ( (rsrc = OpenResFile(*desktop)) == -1) {
  267.             /* Finder hasn't put a desktop on this volume yet? */
  268.             SetResLoad(TRUE);
  269.             HUnlock(desktop);
  270.             continue;
  271.         }
  272.         HUnlock(desktop);
  273.  
  274.         if ( (aH =(ApplHandle) _Get1Resource('APPL', 0)) == NULL) {
  275.             /* Is this a corrupt desktop? Skip it for now, since it may just be a new
  276.               * disk.
  277.               */
  278.             SetResLoad(TRUE);
  279.             CloseResFile(rsrc);
  280.             continue;
  281.         }
  282.  
  283.         SetResLoad(TRUE);
  284.         LoadResource(aH);
  285.  
  286.         HLock(aH);
  287.         end = (Appl *) ( (char *)*aH + GetHandleSize(aH));
  288.     
  289.         /* How many are there? */
  290.         for(a = *aH, volApps = 0; a < end;a = NEXT_APPL(a)) {
  291.             volApps++;            /* total, this volume */
  292.         }
  293.  
  294.         /* Get ready to copy to our application table.  We do what's usually a major no-no,
  295.           * and grow non-relocatables. This is a controlled environment where we can get
  296.           * away with it (Famous last words...)
  297.           */
  298.         if (apps == NULL) {                /* First time */
  299.             if ( (apps = (FullAppl *)NewPtr((Size)(volApps * sizeof(FullAppl)))) == NULL) {
  300.                 SysBeep(10);
  301.                 CloseResFile(rsrc);
  302.                 Quit();
  303.             }
  304.         } else {                        /* Grow it. */
  305.             SetPtrSize(apps, (Size)((numApps + volApps) * sizeof(FullAppl)));
  306.             if (MemErr != noErr) {
  307.                 SysBeep(10);
  308.                 CloseResFile(rsrc);
  309.                 Quit();
  310.             }
  311.         }
  312.         
  313.         /* Copy over the application data to the application table */
  314.         for(a = *aH, i = numApps, copy = apps + numApps; a < end;a = NEXT_APPL(a), copy++) {
  315.             copy->volID  = v.volumeParam.ioVRefNum;
  316.             BlockMove(a, ©->creator, (Size)(sizeof(Appl) + a->name[0]));
  317.         }
  318.  
  319.         numApps += volApps;            /* total, all volumes */
  320.  
  321.         /* Can close this desktop now */
  322.         HUnlock(aH);
  323.         ReleaseResource(aH);
  324.         CloseResFile(rsrc);
  325.     }    
  326.  
  327.     /* Did we find any applications on online HFS volumes? */
  328.     if (numApps == 0) Quit();
  329.  
  330.     /* And quicksort them. */
  331.     if (qsort(apps, numApps, sizeof(FullAppl), appCompare) != 0) {
  332.         SysBeep(5);
  333.         Quit();
  334.     }
  335.  
  336.     if (doDeskDialog) DisposDialog(reading);
  337.     SetCursor(&arrow);
  338.  
  339.     /* Display the modeless dialog box, and then start the event loop. */
  340.     for(;;) {
  341.         dPtr = DispChooseApp();
  342.         op = EventLoop(dPtr, &evRec);
  343.     
  344.         LDispose(lh);    
  345.         DisposDialog(dPtr);
  346.  
  347.         if (op == I_DOCS) {
  348.             SetPt(&pt, 70, 70);
  349.             SFGetFile(pt, "", ListFileFilter, -1, NULL, NULL, &reply);
  350.             
  351.             /* Go around again to resolve the ambiguity about whatever Cancel means. */
  352.             if (reply.good == FALSE) continue;    
  353.         }
  354.         break;
  355.     }
  356.  
  357.     if (op != I_QUIT) {                        /* we have something to launch. */
  358.         /* Follow the example led by StdFile, and set up a HFS working directory. Then SetVol
  359.           * the sucker and launch.
  360.           */
  361.         pb.ioCompletion = NULL;
  362.         pb.ioNamePtr = NULL;
  363.         pb.ioVRefNum = apps[theSelection].volID;
  364.         pb.ioWDProcID = 'H╢R~';                /* application signature. */
  365.         pb.ioWDDirID = apps[theSelection].dir;
  366.  
  367.         if (PBOpenWD(&pb, FALSE) != noErr) {
  368.             StopAlert(R_CORRUPT, (ProcPtr)NULL );
  369.             Quit();
  370.         }
  371.  
  372.         vparm.ioVRefNum = pb.ioVRefNum;
  373.         vparm.ioCompletion = NULL;
  374.         vparm.ioNamePtr = NULL;
  375.     
  376.         if (PBSetVol(&vparm, FALSE) != noErr) {
  377.             StopAlert(R_VOLPROBS, (ProcPtr)NULL );
  378.         } else {
  379.             /* Opened the folder, so we can launch. */
  380.             if (miniFinder && CurApName[0] < 15) {        /* i.e. don't rename! */
  381.                 /* Arrange to return in MiniFinder style. */
  382.                 BlockMove(CurApName, FinderName, CurApName[0] +1);
  383.             } else {
  384.                 /* Make sure the Finder runs. */
  385.                 BlockMove(fName, FinderName, fName[0] + 1);
  386.             }
  387.  
  388.             if (op == I_DOCS) {
  389.                 if (AppParmHandle != NULL) {
  390.                     /* Attach the document to the application parameters. Start by adjusting the
  391.                       * size of AppParmHandle to reflect the document name size, then copy in.
  392.                       */
  393.                     SetHandleSize(AppParmHandle, (Size)(13 + reply.fName[0]));
  394.                     if (MemErr != noErr) {
  395.                         SysBeep(1); SysBeep(1); Quit();
  396.                     }
  397.                     (*((OneArg **)AppParmHandle))->message = appOpen;
  398.                     (*((OneArg **)AppParmHandle))->count = 1;
  399.                     (*((OneArg **)AppParmHandle))->a.versNum = 0;
  400.                     BlockMove(reply.fName, (*((OneArg **)AppParmHandle))->a.fName,
  401.                              (Size)(reply.fName[0] + 1));
  402.                     (*((OneArg **)AppParmHandle))->a.fType = reply.fType;
  403.                     (*((OneArg **)AppParmHandle))->a.vRefNum = reply.vRefNum;
  404.                 }
  405.             }
  406.             
  407.             /* We'd better check that it's there before we leap into the wild blue yonder... */
  408.             v.fileParam.ioCompletion = NULL;
  409.             v.fileParam.ioNamePtr = apps[theSelection].name;
  410.             v.fileParam.ioVRefNum = pb.ioVRefNum;
  411.             v.fileParam.ioFVersNum = 0;
  412.             v.fileParam.ioFDirIndex = 0;
  413.  
  414.             if (PBGetFInfo(&v, FALSE) != noErr)
  415.                 StopAlert(R_CORRUPT, (ProcPtr)NULL );
  416.             else
  417.                 Launch(0, apps[theSelection].name);
  418.         }
  419.     }
  420.     Quit();                            /* To the Finder. */
  421. }
  422.  
  423. /*
  424.   * EventLoop:        sufficient event loop processing for modeless dialogs and desk accessories.
  425.   */
  426.  
  427. int
  428. EventLoop(dPtr, ev)
  429. register DialogPtr     dPtr;
  430. register EventRecord *ev;
  431. {
  432.     short            itemHit;
  433.     Handle            theHand;
  434.     Rect                miniBox;
  435.     short            itemType;
  436.     WindowPtr        whichWindow;
  437.     DialogPtr            theDialog;
  438.     register char        theChar;
  439.     Point                whereIsIt;
  440.     Point                cell;
  441.     GrafPtr            savePort;
  442.     Rect                dragRect;
  443.     Boolean            editDim = TRUE;    /* The edit menu starts out life as dimmed. */
  444.  
  445.     while(1) {
  446.         SystemTask();                    /* Clock keeps on ticking... */
  447.  
  448.         /* Menu consistency. The edit menu should be disabled unless the top window is a
  449.           * system window.
  450.           */
  451.         if (dPtr == FrontWindow() ) {        /* Disable them. */
  452.             if (editDim == FALSE) {
  453.                 editDim = TRUE;
  454.                 DisableItem(GetMHandle(M_EDIT), 0);
  455.                 DrawMenuBar();
  456.             }
  457.         } else {                        /* Enable them. */
  458.             if (editDim == TRUE) {
  459.                 editDim = FALSE;
  460.                 EnableItem(GetMHandle(M_EDIT), 0);
  461.                 DrawMenuBar();
  462.             }
  463.         }
  464.         
  465.         /* What's next? */
  466.         (void)GetNextEvent(everyEvent, ev);
  467.  
  468.         /* First thing we do is check for dialog events. */
  469.         if (IsDialogEvent(ev) == TRUE) {
  470.  
  471.             /* Do our own processing for stuff that DialogSelect won't do. */
  472.             switch(ev->what) {
  473.             case keyDown:
  474.             case autoKey:
  475.                 theChar = ev->message & charCodeMask;
  476.                 if ((ev->modifiers & cmdKey) != 0) {
  477.                     if ((itemHit = DoCommand( MenuKey( theChar ))) != 0) return itemHit;
  478.                 } else {
  479.                     if ( (itemHit = DoChar(dPtr, theChar)) != 0) return itemHit;
  480.                 }
  481.                 continue;
  482.             case activateEvt:
  483.                 LActivate(ev->modifiers&1, lh);
  484.                 break;
  485.             }
  486.  
  487.             if (DialogSelect(ev, &theDialog, &itemHit) == TRUE) {
  488.                 GetPort(&savePort);
  489.                 SetPort(dPtr);
  490.  
  491.                 switch(itemHit) {
  492.                 case I_MINIFINDER:
  493.                     miniFinder ^= 1;
  494.                     GetDItem(dPtr, I_MINIFINDER, &itemType, &theHand, &miniBox);
  495.                     SetCtlValue((ControlHandle)theHand, miniFinder);
  496.                     break;
  497.                 case I_APPS:
  498.                     whereIsIt = ev->where;                /* don't trash original */
  499.                     GlobalToLocal( &whereIsIt );
  500.  
  501.                     /* LClick will follow the mouse until it's released. */
  502.             
  503.                     if (LClick(whereIsIt, 0, lh)  == TRUE) {    /* Double-click somewhere. */
  504.                         LPOINT(cell) = LLastClick(lh);
  505.                         theSelection = cell.v;
  506.                         return( I_RUN );
  507.                     }
  508.             
  509.                     /* Single click. Find out where the selection is now. Can't use LLastClick
  510.                       * because we're interested in where the mouse was *released*.
  511.                       */
  512.                     cell.v = 0;
  513.                     cell.h = 0;
  514.             
  515.                     if (LGetSelect( TRUE, &cell, lh) == FALSE) {
  516.                         /* Insufficient applications to activate scroll bar, and the click is in
  517.                           * the unused portion. This has got to be a floppy!
  518.                           */
  519.                         SysBeep(1);
  520.                         cell.v = theSelection;
  521.                         LSetSelect(TRUE, cell, lh);
  522.                     } else if (theSelection != cell.v) {
  523.                         /* New file type has been selected */
  524.                         theSelection = cell.v; 
  525.                     }
  526.                     break;
  527.                 case I_RUN:
  528.                 case I_QUIT:
  529.                 case I_DOCS:
  530.                     return itemHit;
  531.                 default:
  532.                     break;
  533.                 }
  534.                 SetPort(savePort);
  535.             }
  536.             continue;
  537.         }
  538.  
  539.         /* Doesn't belong to the dialog. Do the normal event processing. */
  540.         switch (ev->what) {
  541.         case mouseDown:
  542.             switch (FindWindow( ev->where, &whichWindow )) {
  543.             case inDesk: 
  544.                 SysBeep(10);
  545.                 break;
  546.             case inMenuBar:
  547.                 if ((itemHit = DoCommand( MenuSelect(ev->where))) != 0)return itemHit;
  548.             case inSysWindow:
  549.                 SystemClick(ev, whichWindow );
  550.                 break;
  551.             case inContent:
  552.                 if (whichWindow != FrontWindow())
  553.                     SelectWindow(whichWindow);
  554.                 break;
  555.             case inDrag:
  556.                 if ( dPtr == whichWindow ) {
  557.                     dragRect = screenBits.bounds;
  558.                     InsetRect(&dragRect, 4, 4);
  559.                     dragRect.top += mbHeight;
  560.                     DragWindow( whichWindow, ev->where, &dragRect );
  561.                 }
  562.                 break;
  563.             }
  564.             break;
  565.         case keyDown:
  566.         case autoKey: 
  567.             theChar = ev->message & charCodeMask;
  568.             if ((ev->modifiers & cmdKey) != 0)  {
  569.                 if ( (itemHit = DoCommand( MenuKey( theChar ))) !=0) return itemHit;
  570.             }
  571.             break;
  572.         }
  573.     }
  574. }
  575.  
  576. /*
  577.   * DoChar:    handles typing at the dialog.
  578.   */
  579.  
  580. int
  581. DoChar(dPtr, theChar)
  582. DialogPtr        dPtr;
  583. unsigned char    theChar;
  584. {
  585.     int                newSelect;
  586.     GrafPtr            savePort;
  587.     Point                cell;
  588.  
  589.     /* Is theChar return or enter? */
  590.     if (theChar == 0x03 || theChar == 0x0d) return (I_RUN);
  591.  
  592.     /* Take the char, and find the first application that starts with this char. */
  593.     GetPort(&savePort);
  594.     SetPort(dPtr);
  595.  
  596.     if ((newSelect = StartsWith(theChar)) != theSelection) {
  597.         /* We've moved. Unhighlight the old. */
  598.         cell.h = 0;
  599.         cell.v = theSelection;
  600.         LSetSelect(FALSE, cell, lh);
  601.  
  602.         /* Highlight the new. */
  603.         theSelection = cell.v =  newSelect;
  604.         LSetSelect(TRUE, cell, lh);
  605.         
  606.         /* And arrange to scroll until visible */
  607.         LAutoScroll(lh);
  608.     }
  609.     SetPort(savePort);
  610.     return 0;
  611. }
  612.  
  613. /*
  614.   * SetupDAs:    setup the desk accessory, file and edit menus.
  615.   */
  616.  
  617. void
  618. SetupDAs()
  619. {
  620.     if ( (mHandle = GetNewMBar(R_MENUBAR)) == NULL) Quit();
  621.     SetMenuBar(mHandle);
  622.  
  623.     /* And add the desk accessories. */
  624.     AddResMenu(GetMHandle(M_ABOUT), 'DRVR' );
  625.  
  626.     DrawMenuBar();
  627. }
  628.  
  629. /*
  630.   * DoCommand:    fields menu commands.
  631.   */
  632.  
  633. int
  634. DoCommand( mResult)
  635. long         mResult;
  636. {
  637.     register int     theItem;
  638.     register int    ret = 0;
  639.     Str255        name;
  640.  
  641.     theItem = LoWord( mResult );
  642.  
  643.     switch (HiWord(mResult)) {
  644.     case M_ABOUT:
  645.         if (theItem == 1) {                    /* It's the about dialog box. */
  646.             About();
  647.         } else {
  648.             GetItem(GetMHandle(M_ABOUT), theItem, name);
  649.             OpenDeskAcc( &name );
  650.         }
  651.         break;
  652.     case M_FILE: 
  653.         /* The items here are Attach Document, a separator, and Quit. */
  654.         ret = (theItem == 1) ? I_DOCS : I_QUIT;
  655.         break;
  656.     case M_EDIT: 
  657.         /* They're only there for the desk accessories. */
  658.         (void)SystemEdit(theItem-1);
  659.         break;
  660.     }
  661.     HiliteMenu(0);
  662.     return ret;
  663. }
  664.  
  665. /*
  666.   * About:            field an "About HD Runner" request.
  667.   */
  668.  
  669. void
  670. About()
  671. {
  672.     DialogPtr    about = GetNewDialog(R_ABOUT, (Ptr)NULL, (WindowPtr)-1);
  673.     short    itemHit;
  674.  
  675.     OutlineButton(about, OK, I_ABOUTOUTLN);    /* Highlight the default button. */
  676.     ShowWindow(about);
  677.     
  678.     do {
  679.         ModalDialog((ProcPtr)NULL, &itemHit);
  680.     } while (itemHit != OK);
  681.     
  682.     DisposDialog(about);
  683. }
  684.  
  685. /*
  686.   * Quit:            an ExitToShell that ensures that we go to the finder.
  687.   */
  688.  
  689. void
  690. Quit()
  691. {
  692.     BlockMove(fName, FinderName, fName[0] + 1);
  693.     ExitToShell();
  694. }
  695.  
  696.  
  697. /*
  698.   * The file filtering routine. Watch the conditions -- it's FALSE to display. 
  699.   */
  700.  
  701. pascal Boolean
  702. ListFileFilter(p)
  703. ParmBlkPtr    p;
  704. {
  705.     if ( p->fileParam.ioFlFndrInfo.fdCreator == apps[theSelection].creator &&
  706.           p->fileParam.ioFlFndrInfo.fdType != 'APPL'                         ) {/* It matched! */
  707.         return FALSE;
  708.     } else {
  709.         return TRUE;                                            /* Do not display */
  710.     }
  711. }
  712.  
  713. /*
  714.   * DispChooseApp:        brings up the modeless dialog box.
  715.   */
  716.  
  717. DialogPtr
  718. DispChooseApp()
  719. {
  720.     DialogPtr        dPtr = GetNewDialog(R_DLOG, (Ptr)NULL, (WindowPtr)-1);
  721.     Handle        theHand;
  722.     int            itemType;
  723.     Point            csize, cell;
  724.     Rect            databounds;
  725.     Rect            listRect;
  726.     register int    i;
  727.     Rect            miniBox;
  728.  
  729.     /* Install the useritem drawing routine. */
  730.     GetDItem(dPtr, I_APPS, &itemType, &theHand, &box);
  731.     SetDItem(dPtr, I_APPS, itemType, ListDraw, &box);
  732.     
  733.     /* Initialize parameters for LNew */
  734.     listRect = box;                        /* struct copy */
  735.     InsetRect(&listRect, 1, 1);            /* prepare for FrameRect */
  736.     listRect.right -= 15;                    /* make room for horiz. scroll bar */
  737.     SetRect(&databounds, 0, 0, 1, 0);        /* list dimensions == 1 row. */
  738.     csize.h = 0;                         /* init csize for defaults */
  739.     csize.v = 0;
  740.     
  741.     /* Declare list and get a handle to it. */
  742.     lh = LNew(&listRect, &databounds, csize, 0, dPtr, FALSE, FALSE, FALSE, TRUE);
  743.     
  744.     /* Specify click, drag, shift-click behavior */
  745.     (*lh)->selFlags = lOnlyOne | lNoNilHilite  | lNoExtend;
  746.     
  747.     /* Declare sufficient cells for our purpose. */
  748.     LAddRow(numApps, 0, lh);
  749.     
  750.     /* Insert the application names into the cells. */
  751.     cell.h = 0;
  752.     cell.v = 0;
  753.  
  754.     for(i = 0; i < numApps; i++, cell.v++) {
  755.         LSetCell(apps[i].name +1, (int)apps[i].name[0], cell, lh);
  756.     }
  757.  
  758.  
  759.     /* And set up the initial minifinder state. */
  760.     GetDItem(dPtr, I_MINIFINDER, &itemType, &theHand, &miniBox);
  761.     SetCtlValue((ControlHandle)theHand, miniFinder);
  762.  
  763.     /* Arrange for highlighting of the default button. */
  764.     OutlineButton(dPtr, I_RUN, I_OUTLINE);
  765.  
  766.     /* And make everything visible */
  767.     ShowWindow(dPtr);
  768.  
  769.     /* Enable drawing */
  770.     LDoDraw(TRUE, lh);
  771.     
  772.     /* Mark the initial selection (first application, first time, then whatever it was we
  773.       * cancelled an Attach Doc on.) 
  774.       */
  775.     cell.h = 0;
  776.     cell.v = theSelection;
  777.     LSetSelect(TRUE, cell, lh);
  778.  
  779.     /* If we aren't at the beginning, arrange to scroll until visible */
  780.     if (theSelection != 0)    LAutoScroll(lh);
  781.  
  782.     return(dPtr);
  783. }
  784.  
  785. /*
  786.   * And the draw routine for the user item.
  787.   */
  788.  
  789. pascal void
  790. ListDraw(theDialog, theItem)
  791. DialogPtr    theDialog;
  792. int        theItem;
  793. {
  794.     FrameRect(&box);                /* Draw the containing box. */
  795.     
  796.     /* And tell the List Manager it's time to redraw. */    
  797.     LUpdate( ((WindowPeek)theDialog)->port.visRgn, lh);
  798. }
  799.  
  800. /*
  801.   * _Get1Resource:        a GetResource for the current resource file only.
  802.   */
  803.  
  804. Handle
  805. _Get1Resource(res, id)
  806. ResType    res;
  807. int        id;
  808. {
  809.     register Handle    h;
  810.  
  811.     if (has64K) {
  812.         h = GetResource(res, id);
  813.         return( (HomeResFile(h) != CurMap) ? (Handle)NULL : h);
  814.     } else {
  815.         return (Get1Resource(res, id));
  816.     }
  817. }
  818.  
  819. /*
  820.   * appCompare:    qsort comparison function.
  821.   */
  822.  
  823. int
  824. appCompare(a1, a2)
  825. FullAppl    *a1;
  826. FullAppl    *a2;
  827. {
  828.     Str255    name;
  829.  
  830.     /* Do a name comparison. */
  831.     return (IUCompString(a1->name, a2->name));
  832. }
  833.  
  834. /*
  835.   * StartsWith:        utility routine to do first character match for scrolling. Matches up to 
  836.   *                MAX_MATCH characters and then clamps until timeout.
  837.   */
  838.  
  839. /* State data for multiple character match. */
  840. #define    MAX_MATCH        4
  841. #define    KEY_MAGNIFIER    2        /* The value used by StdFile. Thanks, MacNosy! */
  842.  
  843. static int        matchLen = 1;
  844. static char      match[MAX_MATCH +1];
  845. static ulong     lastKey = 0;
  846.  
  847. int
  848. StartsWith(c)
  849. Byte    c;
  850. {
  851.     register int        i;
  852.     unsigned char        pre[MAX_MATCH +1];
  853.     register int        cmpLen;
  854.     register ulong        thresh;
  855.     register FullAppl    *a;
  856.  
  857.     /* Check for cursor up, down. */
  858.     if (c == cursDown) {
  859.         /* Go up one, clamp at the end. */
  860.         lastKey = Ticks;
  861.         matchLen = 1;
  862.         return ((theSelection == numApps -1)? theSelection : theSelection +1);
  863.     }
  864.     
  865.     if (c == cursUp) {
  866.         lastKey = Ticks;
  867.         matchLen = 1;
  868.  
  869.         /* Go down one, clamp at the bottom. */
  870.         return((theSelection == 0)? 0: theSelection -1);
  871.     }
  872.  
  873.     if (lastKey != 0) {
  874.         /* Have a timestamp. Are we still within the time interval for matching? */
  875.         thresh = (ulong)Ticks - (ulong) (KeyThresh * KEY_MAGNIFIER);
  876.  
  877.         if (thresh < lastKey) {
  878.             /* Within the interval. Check the MAX_MATCH clamp. */
  879.             if (matchLen  +1<= MAX_MATCH) {
  880.                 /* Add another character to the match string. */
  881.                 matchLen++;
  882.             }
  883.         } else {
  884.             /* Waited too long. Reset the count. */
  885.             matchLen = 1;
  886.         }
  887.     } else {
  888.         /* Starting things off. */
  889.         matchLen = 1;    
  890.     }
  891.  
  892.     lastKey = (ulong) Ticks;
  893.     match[0] = matchLen;
  894.     match[matchLen] = c;
  895.     UprString(match, FALSE);
  896.  
  897.     /* Use the International Utilities package to handle case differences. */
  898.     for(i = 0, a = apps; i < numApps; i++, a++) {
  899.         cmpLen = MIN(matchLen,  apps[i].name[0]);
  900.         BlockMove(a->name +1, pre +1, cmpLen);
  901.         pre[0] = cmpLen;
  902.         UprString(pre, FALSE);
  903.  
  904.         if (pstrncmp(pre +1, match +1, cmpLen) >= 0) return i;
  905.     }
  906.     return (numApps -1);
  907. }
  908.  
  909. /*  
  910.   * Pascal string version of strncmp. Used for comparing processed strings (UprString) & avoiding
  911.   * the overhead of calling IUMagString. Not very general purpose - it assumes that each string is
  912.   * at least n chars long. 
  913.   */
  914.  
  915. int
  916. pstrncmp(s1, s2, n)
  917. register char *s1, *s2;
  918. register int n;
  919. {
  920.     for (; --n && (*s1 == *s2); s1++, s2++) ;
  921.         
  922.     return (*s1 - *s2);
  923. }
  924.